package com.exmaple.lemlin;

import android.text.TextUtils;

import com.exmaple.lemlin.utils.LogUtils;
import com.exmaple.lemlin.xmpp.XmppConnection;

import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jivesoftware.smackx.iqregister.AccountManager;
import org.jivesoftware.smackx.offline.OfflineMessageManager;
import org.jivesoftware.smackx.ping.PingManager;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Localpart;
import org.jxmpp.stringprep.XmppStringprepException;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Timer;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Function3;
import io.reactivex.observables.GroupedObservable;

public class XMPPConnection {


    private final static String XMPP_SERVICE_DONAIN = "101.37.253.11";
    private final static String XMPP_SERVICE_HOST = "101.37.253.11";
    private final static int XMPP_SERVICE_PORT = 5222;

    private static volatile XMPPConnection mXmppConnection = null;
    private AbstractXMPPConnection connection = null;
//    private Observable<AbstractXMPPConnection> observableConnection = null;


    public static XMPPConnection getInstance() {
        if (mXmppConnection == null) synchronized (XMPPConnection.class) {
            if (mXmppConnection == null)
                mXmppConnection = new XMPPConnection();
        }
        return mXmppConnection;
    }

    private Observable<AbstractXMPPConnection> openConnection1() {
        return Observable.just(connection).groupBy(new Function<AbstractXMPPConnection, Boolean>() {
            @Override
            public Boolean apply(AbstractXMPPConnection abstractXMPPConnection) throws Exception {
                return abstractXMPPConnection != null;
            }
        }).flatMap(new Function<GroupedObservable<Boolean, AbstractXMPPConnection>, ObservableSource<AbstractXMPPConnection>>() {
            @Override
            public ObservableSource<AbstractXMPPConnection> apply(GroupedObservable<Boolean, AbstractXMPPConnection> booleanConnection) throws Exception {
                if (booleanConnection.getKey())
                    return booleanConnection.map(new Function<AbstractXMPPConnection, AbstractXMPPConnection>() {
                        @Override
                        public AbstractXMPPConnection apply(AbstractXMPPConnection abstractXMPPConnection) throws Exception {
                            return abstractXMPPConnection;
                        }
                    });
                return Observable.just(XMPPTCPConnectionConfiguration.builder())
                        .map(new Function<XMPPTCPConnectionConfiguration.Builder, XMPPTCPConnectionConfiguration>() {
                            @Override
                            public XMPPTCPConnectionConfiguration apply(XMPPTCPConnectionConfiguration.Builder builder) throws Exception {
                                return builder.setXmppDomain(XMPP_SERVICE_DONAIN)
                                        .setHostAddress(InetAddress.getByName(XMPP_SERVICE_HOST))
                                        .setPort(XMPP_SERVICE_PORT)
                                        .setSendPresence(false)
                                        .setCompressionEnabled(true)
                                        .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                                        .build();
                            }
                        }).map(new Function<XMPPTCPConnectionConfiguration, AbstractXMPPConnection>() {
                            @Override
                            public AbstractXMPPConnection apply(XMPPTCPConnectionConfiguration config) throws Exception {
                                //初始化连接
                                connection = new XMPPTCPConnection(config);
                                //连接监听
                                connection.addConnectionListener(new ConnectionListener() {
                                    @Override
                                    public void connected(org.jivesoftware.smack.XMPPConnection connection) {

                                    }

                                    @Override
                                    public void authenticated(org.jivesoftware.smack.XMPPConnection connection, boolean resumed) {

                                    }

                                    @Override
                                    public void connectionClosed() {

                                    }

                                    @Override
                                    public void connectionClosedOnError(Exception e) {

                                    }
                                });
                                //用户状态监听
                                connection.addSyncStanzaListener(new StanzaListener() {
                                    @Override
                                    public void processStanza(Stanza packet) {

                                    }
                                }, new AndFilter(new StanzaTypeFilter(Presence.class)));
                                return connection;
                            }
                        });
            }
        });
    }


    /**
     * 打开连接
     */
    public AbstractXMPPConnection openConnection() {
        if (connection == null) try {
            XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                    //设置openfire服务器名称
                    .setXmppDomain(XMPP_SERVICE_DONAIN)
                    //设置openfire主机IP
                    .setHostAddress(InetAddress.getByName(XMPP_SERVICE_HOST))
                    //设置端口号：默认5222
                    .setPort(XMPP_SERVICE_PORT)
                    //设置离线状态
                    .setSendPresence(false)
                    //设置开启压缩，可以节省流量
                    .setCompressionEnabled(true)
                    //禁用SSL连接
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                    .build();
            //初始化连接
            connection = new XMPPTCPConnection(config);
            //连接监听
            connection.addConnectionListener(null);
            //用户状态监听
            //connection.addSyncStanzaListener((null), new AndFilter(new StanzaTypeFilter(Presence.class)));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 关闭连接
     */
    public void closeConnection() {
        if (connection != null && connection.isConnected()) {
            connection.disconnect();
        }
    }

    /**
     * 判断连接是否连接
     *
     * @return true/false
     */
    public boolean isConnected() {
        return connection != null && connection.isConnected();
    }

    /**
     * 判断连接是否通过了身份验证
     *
     * @return true/false
     */
    public boolean isAuthenticated() {
        return connection != null && connection.isConnected() && connection.isAuthenticated();
    }

    /**
     * 用户登录
     *
     * @param userName 用户名
     * @param password 用户密码
     * @return true/false
     */
    public Observable<Boolean> accessSignIn(String userName, String password) {

        return Observable.zip(Observable.just(userName), Observable.just(password), openConnection1(), new Function3<String, String, AbstractXMPPConnection, Boolean>() {
            @Override
            public Boolean apply(String s, String s2, AbstractXMPPConnection abstractXMPPConnection) throws Exception {
                abstractXMPPConnection.connect().login(s, s2);
                boolean isAuthenticated = abstractXMPPConnection.isAuthenticated();
                if (isAuthenticated) {
                    //ping请求
                    PingManager pingManager = PingManager.getInstanceFor(abstractXMPPConnection);

                    //获取离线消息
                    OfflineMessageManager manager = new OfflineMessageManager(abstractXMPPConnection);
                    manager.getMessages();
                    manager.deleteMessages();

                }
                return isAuthenticated;
            }
        });
    }

    /**
     * 用户注册
     *
     * @param userName 用户名
     * @param password 用户密码
     * @param map      其他信息
     * @return true/false
     */
    public boolean accessSignOut(String userName, String password, Map<String, String> map) throws Exception {
        try {
            if (!isConnected()) {
                connection.connect();
            }
            AccountManager account = AccountManager.getInstance(connection);
            account.sensitiveOperationOverInsecureConnection((true));
            account.createAccount(Localpart.from(userName), password, map);
            return true;
        } catch (SmackException | IOException | XMPPException | InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 获取所有好友信息
     *
     * @return List<RosterEntry>
     */
    public List<RosterEntry> getRosterEntrys() {
        if (!isConnected()) {
            throw new NullPointerException("服务器连接失败，请先连接服务器！");
        }
        return new ArrayList<>(Roster.getInstanceFor(connection).getEntries());
    }


    /**
     * 创建聊天窗口
     *
     * @param toName 接收者用户名(admin@主机名)
     * @return Chat
     */
    public Chat createChatWith(String toName) {
        if (!isConnected()) {
            throw new NullPointerException("服务器连接失败，请先连接服务器！");
        }
        if (!toName.contains("@")) {
            throw new IllegalArgumentException("用户名错误！例如：admin@xxx.example.org");
        }
        try {
            return ChatManager.getInstanceFor(connection)
                    .chatWith(JidCreate.entityBareFrom(toName));
        } catch (XmppStringprepException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 发送消息
     *
     * @param toName  接收者用户名
     * @param message 发送数据
     */
    public void sendChatMessage(String toName, Message message) {
        try {
            this.createChatWith(toName).send(message);
        } catch (SmackException.NotConnectedException | InterruptedException e) {
            e.printStackTrace();
        }
    }


}
